#include <pthread.h>
#include <unistd.h>
#include <queue>

template<class T>
class BlockQueue
{
public:
   BlockQueue(size_t maxline, size_t minline = 0)
      :_max_line(maxline), _min_line(minline)
   {
      pthread_mutex_init(&_mutex, nullptr);
      pthread_cond_init(&_prod_cond, nullptr);
      pthread_cond_init(&_consum_cond, nullptr);
   }

   void push(const T& obj)
   {
      pthread_mutex_lock(&_mutex);
      if (size() >= _max_line) {
	 pthread_cond_signal(&_consum_cond);
	 pthread_cond_wait(&_prod_cond, &_mutex);
      }
      _queue.push(obj);
      pthread_mutex_unlock(&_mutex);
   }

   T pop()
   {
      pthread_mutex_lock(&_mutex);
      if (size() <= _min_line) {
	 pthread_cond_signal(&_prod_cond);
	 pthread_cond_wait(&_consum_cond, &_mutex);
      }
      T obj = _queue.front();
      _queue.pop();
      pthread_mutex_unlock(&_mutex);
      return obj;
   }

   bool empty()
   {
      return _queue.empty();
   }

   size_t size()
   {
      return _queue.size();
   }

   ~BlockQueue()
   {
      pthread_mutex_destroy(&_mutex);
      pthread_cond_destroy(&_prod_cond);
      pthread_cond_destroy(&_consum_cond);
   }

private:
   std::queue<T> _queue;
   size_t _max_line;
   size_t _min_line;
   pthread_mutex_t _mutex;
   pthread_cond_t _prod_cond;
   pthread_cond_t _consum_cond;
};
